import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.util.ArrayList;
import java.util.Iterator;

public class SampleAndHold extends DrawObject
{
	// DrawObject abilities:
	@Override
	public boolean IsSampleAndHold()
	{
		return true;
	}

	@Override
	public boolean CanBeDeletedByUser()
	{
		return true;
	}

	@Override
	public boolean CanBeExcitedByUser()
	{
		return true;
	}

	@Override
	public boolean CanBeMovedByUser()
	{
		return true;
	}

	@Override
	public boolean CanBeSelectedByUser()
	{
		return true;
	}
	// end of DrawObject abilities

	private ArrayList<SampleAndHoldInputNode> InputNodes = new ArrayList<SampleAndHoldInputNode>();
	private ArrayList<SampleAndHoldOutputNode> OutputNodes = new ArrayList<SampleAndHoldOutputNode>();

	private final int StrengthMax = 78;
	private int ExcitementTicksRemaining = 0;

	public SampleAndHold(Point Pos)
	{
		SetPos(Pos);
		SetSize(new Point(100, 50));
		InitializeExcitement(ExcitementOnceMilliseconds);

		// simulate TWO TextField activations although there was only one:
		SetStrength(Simulation.TicksPerIOTextAreaLine());
	}

	@Override
	protected void AddExcitementForNextTick(double ExcitementAdd)
	{
		// uses the ExciteAsPartner() excitement donation scheme!
	}

	public SampleAndHoldInputNode AddInputNode()
	{
		SampleAndHoldInputNode SampleAndHoldInputNodeNew = new SampleAndHoldInputNode(new Point(Pos.x - 10, Pos.y + InputNodes.size() * 20));

		SampleAndHoldInputNodeNew.SetCompoundId(((SampleAndHold) this).CompoundId);
		SampleAndHoldInputNodeNew.ExcitementPartner = (SampleAndHold) this;

		InputNodes.add(SampleAndHoldInputNodeNew);

		return SampleAndHoldInputNodeNew;
	}

	public void AddInputNode(SampleAndHoldInputNode AddNode)
	{
		AddNode.ExcitementPartner = (SampleAndHold) this;

		InputNodes.add(AddNode);
	}

	public SampleAndHoldOutputNode AddOutputNode()
	{
		SampleAndHoldOutputNode SampleAndHoldOutputNodeNew = new SampleAndHoldOutputNode(new Point(Pos.x + 100, Pos.y + Size.y / 2));

		SampleAndHoldOutputNodeNew.SetCompoundId(((SampleAndHold) this).CompoundId);

		((SampleAndHold) this).ExcitementPartner = SampleAndHoldOutputNodeNew;

		OutputNodes.add(SampleAndHoldOutputNodeNew);

		return SampleAndHoldOutputNodeNew;
	}

	public void AddOutputNode(SampleAndHoldOutputNode AddNode)
	{
		((SampleAndHold) this).ExcitementPartner = AddNode;

		OutputNodes.add(AddNode);
	}

	@Override
	protected void AfterCollectingAllExcitementForNextTick()
	{
		// uses the ExciteAsPartner() excitement donation scheme!
	}

	@Override
	public void Draw(Graphics g)
	{
		if (!(IsWithinWindowBounds(this, 0)))
			return;

		// background (overdraw grid)
		if (IsHighlighted || IsPartOfMultiSelection)
			SetColor(g, Color.GREEN);
		else
			SetColor(g, Color.LIGHT_GRAY);
		FillRect(g, Pos.x, Pos.y, Size.x, Size.y);

		// strength bar
		SetColor(g, Color.YELLOW);
		FillRect(g, Pos.x, Pos.y, 1 + Strength * 80 / StrengthMax, 20);

		// strength bar frame
		if (IsHighlighted || IsPartOfMultiSelection)
			SetColor(g, Color.BLACK);
		else
			SetColor(g, GetExcitementColor());
		DrawRect(g, Pos.x, Pos.y, 80, 20);

		// frame
		if (IsHighlighted || IsPartOfMultiSelection)
			SetColor(g, Color.BLACK);
		else
			SetColor(g, GetExcitementColor());
		DrawRect(g, Pos.x, Pos.y, Size.x, Size.y);

		// strength value text
		SetColor(g, Color.BLACK);
		g.setFont(GetSmallFont());
		DrawString(g, ((Integer) Strength).toString(), Pos.x + 80 + 3, Pos.y + 15);

		// info text
		DrawString(
			g, "sim. " + (int) (Strength / Simulation.TicksPerIOTextAreaLine()) + " add.", // ATTENTION: keep in mind Lines could delay excitement, so look closely if debugging!
			Pos.x + 3, Pos.y + 20 + 15 - 2
		);
		DrawString(g, "IO p. lines", Pos.x + 3, Pos.y + 20 + 15 + 15 - 4);
	}

	protected void ExciteAsPartner(double ExcitementAdd, DrawObject Sender)
	{
		ExcitementCurrent += LimitExcitement(ExcitementAdd);

		if (DoesActivate(ExcitementCurrent))
		{
			ExcitementTicksRemaining = Strength; // just set to maximal value, also if there are still ticks remaining from any previous excitement
		}
	}

	@Override
	public ArrayList<DrawObject> GetChildDrawObjects()
	{
		ArrayList<DrawObject> ChildDrawObjects = new ArrayList<DrawObject>();

		for (Iterator<SampleAndHoldInputNode> i = InputNodes.iterator(); i.hasNext();)
		{
			SampleAndHoldInputNode o = i.next();
			ChildDrawObjects.add(o);
		}
		for (Iterator<SampleAndHoldOutputNode> i = OutputNodes.iterator(); i.hasNext();)
		{
			SampleAndHoldOutputNode o = i.next();
			ChildDrawObjects.add(o);
		}

		return ChildDrawObjects;
	}

	@Override
	protected void LoseExcitementAndAddExcitementForNextTick()
	{
		// SampleAndHold-specific: actually we don't need
		// ExcitementCurrent and ExcitementAddForNextTick at all,
		// just excite output node as long as excitement ticks
		// remaining!

		ExcitementCurrent = LimitExcitement(ExcitementCurrent);

		ShiftExcitementHistory(ExcitementCurrent);

		// SampleAndHold-specific:
		if (ExcitementTicksRemaining > 0)
		{
			// if (!(IsActivated()))
			{
				((SampleAndHoldOutputNode) ExcitementPartner).ExciteAsPartner(
					1.0, // just the default excitement value
					this
				);

				ExcitementTicksRemaining--;
			}
		}
		// end of specific

		// reset
		ExcitementCurrent = 0.0;
		ExcitementAddForNextTick = 0.0;
	}

	@Override
	public void Move(Point MoveAmount)
	{
		for (Iterator<SampleAndHoldInputNode> i = InputNodes.iterator(); i.hasNext();)
		{
			SampleAndHoldInputNode o = i.next();
			o.Move(MoveAmount);
		}
		for (Iterator<SampleAndHoldOutputNode> i = OutputNodes.iterator(); i.hasNext();)
		{
			SampleAndHoldOutputNode o = i.next();
			o.Move(MoveAmount);
		}

		Pos.x += MoveAmount.x;
		Pos.y += MoveAmount.y;
	}

	@Override
	public void SetHighlighted(boolean IsHighlightedNew)
	{
		IsHighlighted = IsHighlightedNew;

		for (Iterator<SampleAndHoldInputNode> i = InputNodes.iterator(); i.hasNext();)
		{
			SampleAndHoldInputNode o = i.next();
			o.SetHighlighted(IsHighlighted);
		}
		for (Iterator<SampleAndHoldOutputNode> i = OutputNodes.iterator(); i.hasNext();)
		{
			SampleAndHoldOutputNode o = i.next();
			o.SetHighlighted(IsHighlighted);
		}
	}

	@Override
	public void SetPartOfMultiSelection(boolean IsPartNew)
	{
		IsPartOfMultiSelection = IsPartNew;

		for (Iterator<SampleAndHoldInputNode> i = InputNodes.iterator(); i.hasNext();)
		{
			SampleAndHoldInputNode o = i.next();
			o.SetPartOfMultiSelection(IsPartOfMultiSelection);
		}
		for (Iterator<SampleAndHoldOutputNode> i = OutputNodes.iterator(); i.hasNext();)
		{
			SampleAndHoldOutputNode o = i.next();
			o.SetPartOfMultiSelection(IsPartOfMultiSelection);
		}
	}

	@Override
	public void SetPos(Point PosNew)
	{
		PosNew = AlignOnGrid(PosNew);

		Point MoveAmount = new Point(PosNew.x - Pos.x, PosNew.y - Pos.y);

		Move(MoveAmount);
	}

	public void Toggle()
	{
		Strength++;
		if (Strength > StrengthMax)
			Strength = 0;
	}

	public void ToggleIsNegative()
	{
		Strength += Simulation.TicksPerIOTextAreaLine();
		Strength = ((int) (Strength / Simulation.TicksPerIOTextAreaLine())) * Simulation.TicksPerIOTextAreaLine(); // round down
		if (Strength > StrengthMax)
			Strength = 0;
	}
}
